import {
    BufferAttribute,
    Color,
    DoubleSide,
    Mesh,
    PlaneGeometry,
    ShaderMaterial,
    Vector3
} from "three";

const colors1 = [
    0x005eff,
    0x1069ff,
    0x2175ff,
    0x3180ff,
    0x418cff,
    0x5197ff,
    0x62a3ff,
    0x72aeff,
    0x82b9ff,
    0x93c5ff,
    0xa3d0ff,
    0xb3dcff,
    0xc3e7ff,
    0xd4f3ff,
    0xe4feff
]
const colors2 = [
    0xd9f0ff,
    0xa7e10f,
    0x75c21e,
    0x5cb326,
    0x2a9553, // land
    0x5cb326,
    0x75c21e,
    0xa7e10f,
    0xd9f0ff,
]
const colors3 = [
    0xffe3f8,
    0xffd3e6,
    0xffc3d5,
    0xffb2c3,
    0xffa2b1,
    0xff929f,
    0xff828e,
    0xff727c,
    0xff616a,
    0xff5159,
    0xff4147,
    0xff3135,
    0xff2023,
    0xff1012,
    0xff0000
]

const color2Len = colors2.length

export let colorBackground = 0xd7d7d7 // 背景色

const len = colors1.length+colors2.length+colors3.length // 总色数
const standardIndex = Math.ceil(len/2) // 水平面位置

const backgroundIndex = 999

let vertexShader = `
varying vec3 vColor;
uniform vec3 aColors[${len}];
uniform vec3 background;
attribute float c;

void main() {
  int index = int(c);
  if (index == ${backgroundIndex}) {
      vColor = background;
  } else {
      vColor = aColors[index];
  }
  vec3 newPosition = position + normal * 0.05;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`
const fragmentShader = `
varying vec3 vColor;
void main() {
  gl_FragColor = vec4(vColor, 1.0);
}
`

const genColor = (hex: number): Vector3 => {
    let c = new Color(hex)
    return new Vector3(c.r, c.g, c.b)
}

export class LineData {
    max: number = 0;
    min: number = 0;
    standard: number = 0
    data: number[]=[]
}


export class HotMap {
    mesh: Mesh
    geometry?: PlaneGeometry
    material!: ShaderMaterial
    particlePosition: Float32Array
    lineSize!: number
    pointsFilterSize: number
    lines!:number
    colors:Color[]

    step: number
    uniforms: {
        aColors: { value: Color[] }
        background: {value: Color }
    }
    threshold: number
    extendSize: number
    backgroundColor: Color
    retina: number = 3// 默认3, 必须为单数
    constructor() {
        this.mesh = new Mesh()
        this.extendSize = 5.0
        this.step = 0.1
        this.particlePosition = new Float32Array()
        // this.backgroundColor = new Color(0x78b1e5)
        this.backgroundColor = new Color(colorBackground)
        this.colors = []
        this.initColors()

        this.uniforms = {
            aColors: {value: this.colors},
            background: {value: this.backgroundColor}
        }

        this.material = new ShaderMaterial({
            vertexColors: true,
            side: DoubleSide,
            fragmentShader: fragmentShader,
            vertexShader: vertexShader,
            uniforms: this.uniforms
        })
        this.material.uniforms.aColors.value = this.colors
        // console.log(this.uniforms)

        this.mesh.rotateX(-Math.PI / 2)
    }

    setPointsFilter(size: number) {
        this.pointsFilterSize = size
    }

    private initColors() {
        for (let i=0;i<colors1.length;i++) {
            let c = colors1[i]
            this.colors.push(genColor(c))
        }
        for (let i=0;i<colors2.length;i++) {
            let c = colors2[i]
            this.colors.push(genColor(c))
        }
        for (let i=0;i<colors3.length;i++) {
            let c = colors3[i]
            this.colors.push(genColor(c))
        }
    }

    private calcColorIndex(level: number): number {
        let lvl = level+standardIndex
        if (lvl < 0) {
            lvl = 0
        }
        if (lvl >= len) {
            lvl = len - 1// 越界检查
        }
        return lvl
    }

    init(lines: number, eachLine: number, threshold: number, step:number) {
        this.lines = lines
        this.lineSize = eachLine
        this.threshold = threshold
        this.step = step
        let width = 12
        let height = lines/eachLine*12

        if (this.geometry != undefined) {
            let g = this.geometry
            this.geometry = undefined
            g.dispose()
        }

        this.geometry = new PlaneGeometry(width, height, eachLine*this.retina-1,lines*this.retina-1)

        let colorMap = new Float32Array(this.lines*this.lineSize*this.retina*this.retina)
        for (let i=0;i<colorMap.length;i++) {
            colorMap[i] = backgroundIndex
        }
        this.geometry.setAttribute('c', new BufferAttribute(colorMap, 1))
        // console.log("create hot_map")
        this.mesh.geometry = this.geometry!
        this.mesh.material = this.material!
        let attr = this.geometry.getAttribute('color')
        attr?.needsUpdate(true)
    }

    addLine(items: LineData[]) {
        // if (items.length > 0) {
        //     return
        // }
        let inputLineN = items.length // 输入了多少行
        let colors = this.geometry?.getAttribute('c')
        let vertices = colors!.array

        let moveStart = 0;
        let moveEnd = this.lines-inputLineN;
        // console.log("move lines", moveStart, "<-", moveEnd)
        let linePoints = this.lineSize*this.retina// 一行的点数
        let totalCopy = (moveEnd-moveStart)*this.retina*linePoints// 被复制的开始位置
        let copyStartPoint = linePoints*this.retina // 复制总数

        // console.log('总点数', this.lineSize*this.lines*this.retina*this.retina)
        // console.log('实际总点数', vertices.length)
        // console.log('move end', moveEnd)
        // console.log('复制开始点', copyStartPoint)
        // console.log('总复制点数', totalCopy)
        for (let i=0; i<totalCopy; i++) { // 数据前移N行
            vertices[i] = vertices[copyStartPoint+i]
        }

        // console.log("update lines", this.lines - inputLineN, "<-", this.lines)
        for (let i = 0; i < inputLineN; i++) { // 更新N行, 从第一行开始
            let lineNum = this.lines-inputLineN+i
            let item = items[i]
            this.addOneLine(item, lineNum, vertices)
        }

        // console.log(position)
        this.geometry?.computeVertexNormals()
        colors!.needsUpdate = true
        let attr = this.geometry?.getAttribute("color")
        attr?.needsUpdate(true)
    }
    addOneLine(line: LineData, lineNum: number, vertices: any) {
        if (line == undefined) {
            return
        }
        if (!line.data || line.data.length == 0) {// 空白行, 使用背景色填充
            // empty line
            line.data = []
            for (let i=0; i<this.lineSize; i++) {
                line.data.push(backgroundIndex)
            }
            for (let j = 0; j < this.lineSize; j++) {// 将新行放到最后一行
                this.retinaColor(vertices, lineNum, j, this.lineSize, this.retina, line.data[j])
            }
            return
        }
        let threshold = line.standard
        let step = (line.max - threshold) / (color2Len/2)
        let extendStart = Math.floor(color2Len/2)
        let extendStep = this.extendSize / 15
        // 如果数据少(lineSize)
        let needAdd = this.lineSize - line.data.length

        // console.log('input数量', line.data.length)
        if (needAdd > 0) {
            // console.log("需要增加数据", needAdd)
            for (let i =0; i < needAdd; i++) {
                line.data.push(threshold)
            }
        }
        // console.log('input数据')
        // console.log(line.data)
        let length = line.data.length
        if (this.pointsFilterSize > 0) { // 两边整理平坦
            for (let i = 0; i < this.pointsFilterSize; i++) {
                line.data[i] = threshold // 左
                line.data[length - 1 - i] = threshold // 右
            }
        }

        for (let i = 0; i < this.lineSize; i++) {

            let y = line.data[i]
            let level = 0
            if (y > line.min && y < line.max) {
                level = (y - line.min) / step - extendStart
            } else if (y < line.min) {
                level = (y - line.min) / extendStep -extendStart
            } else {
                level = (y - line.max) / extendStep + extendStart
            }
            // let level = this.calcLevel(y, threshold, validStep)
            let colorIndex = this.calcColorIndex(level)
            if (colorIndex < 0) {
                colorIndex = 0
            }
            if (colorIndex >= len) {
                colorIndex = len - 1
            }
            line.data[i] = colorIndex
        }
        // console.log('调整后,颜色值')
        // console.log(line.data)
        // console.log('添加一行到第', lineNum)
        for (let j = 0; j < this.lineSize; j++) {// 将新行放到最后一行
            this.retinaColor(vertices, lineNum, j, this.lineSize, this.retina, line.data[j])
        }
    }

    /**
     *
     * @param vertices 颜色数组
     * @param x 逻辑坐标x
     * @param y 逻辑坐标y
     * @param lineSize 逻辑整行坐标数
     * @param retinaSize 视网膜宽度, 例如:n, 实际点数为n*n
     * @param colorIndex 颜色值
     */
    retinaColor(vertices: any, x: number, y: number, lineSize: number,retinaSize:number, colorIndex: number): void {
        // console.log('line_num:', x, 'index:', y)
        for (let i=0; i < retinaSize; i++) {
            let pos = (x*retinaSize+i)*lineSize*retinaSize+y*retinaSize
            // console.log("首节点", pos)
            for (let j = 0; j < retinaSize; j++) {
                vertices[pos+j] = colorIndex
            }
        }
    }

}
